package cn.you.GenghisKhan.common.dataset;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Time;
import java.util.Date;
/**
 * 通用二进制转换方法
 */
public class Record {
	public static byte Version = 1;// 版本号

	public static void WriteTypeValue(ByteArrayOutputStream stream,
			DataType type, Object value) throws IOException {
		if (value == null) {
			// 写入0x00表示是空值
			stream.write((byte) 0x00);
			return;
		}
		/*
		 * if (value==DBNull.Value) { //写入0x10表示是数据库空值 stream.write((byte)0x10);
		 * return; }
		 */
		// 写入非0表示非空值
		switch (type) {
		case Int16: {
			writeB2Value(stream, Short.valueOf(value.toString()));
		}
			break;
		case Int32: {
			writeB4Value(stream, (Integer) value);
		}
			break;
		case Int64: {
			writeB8Value(stream, (Long) value);
		}
			break;
		case Bool: {
			boolean bValue = (value.equals("1") || value.equals(true)) ? true
					: false;
			stream.write(bValue ? (byte) 0x81 : (byte) 0x80);
		}
			break;
		case Double: {
			long l = Double.doubleToLongBits((Double) value);
			writeB8Value(stream, (Long) l);
		}
			break;
		case Float:
			int f = Float.floatToIntBits((Float) value);
			writeB4Value(stream, f);
			break;
		case Decimal:
			BigDecimal bd = (BigDecimal) value;
			byte[] bdbytes = bd.unscaledValue().toByteArray();
			int n = bdbytes.length;
			writeB4Value(stream, n);
			if (n > 0) {
				stream.write(bdbytes, 0, bdbytes.length);
			}
			break;
		case Char:
			// writeB2Value(stream, (short)(char)(Character)value);
			writeB2Value(stream, Short.parseShort((String) value));

			break;
		case String:
			byte[] btsStr = (value.toString()).getBytes("UTF-8");
			writeB4Value(stream, btsStr.length);
			if (btsStr.length > 0) {
				stream.write(btsStr, 0, btsStr.length);
			}
			break;
		case Date:
			Date date = (Date) value;
			long dl = date.getTime();
			writeB8Value(stream, dl);
			break;
		case Time:
			Time time = (Time) value;
			long tl = time.getTime();
			writeB8Value(stream, tl);
			break;
		case TimeSpan:
			java.sql.Timestamp timespan = (java.sql.Timestamp) value;
			long timespanl = timespan.getTime();
			writeB8Value(stream, timespanl);
			break;
		case Bytes:
			/*
			 * ByteArrayOutputStream bos = new ByteArrayOutputStream();
			 * ObjectOutputStream oos = new ObjectOutputStream(bos);
			 * oos.writeObject(value); oos.flush(); bos.toByteArray ();
			 */
			byte[] btsBts = (byte[]) value;
			int btsn = btsBts.length;

			writeB4Value(stream, btsn);
			if (btsn > 0) {
				stream.write(btsBts, 0, btsn);
			}

			break;
		case Object:
			byte[] btsobjets = toByteArray(value);
			int btsobjetsn = btsobjets.length;

			writeB4Value(stream, btsobjetsn);
			if (btsobjetsn > 0) {
				stream.write(btsobjets, 0, btsobjetsn);
			}
			break;
		}
	}

	public static Object ReadTypeValue(ByteArrayInputStream stream,
			DataType type) throws UnsupportedEncodingException {
		Object value = null;
		byte[] flagBts = new byte[1];
		byte[] buffer = new byte[0x10];
		if (flagBts.length != stream.read(flagBts, 0, flagBts.length)) {
			return null;
		}
		if (flagBts[0] == (byte) 0x00) {
			value = null;
			return null;
		}
		switch (type) {
		case Int16: {
			Short outValue = 0;
			value = readB2Value(stream, flagBts[0], outValue);
		}
			break;
		case Int32: {
			int outValue = 0;
			value = readB4Value(stream, flagBts[0], outValue);
		}
			break;
		case Int64: {
			long outValue = 0;
			value = readB8Value(stream, flagBts[0], outValue);
		}
			break;
		case Bool: {
			value = (flagBts[0] != (byte) 0x80);
		}
			break;
		case Double: {
			long outValue = 0;
			long l = readB8Value(stream, flagBts[0], outValue);
			value = Double.longBitsToDouble(l);
		}
			break;
		case Float:
			int outfValue = 0;
			int f = readB4Value(stream, flagBts[0], outfValue);
			value = Float.intBitsToFloat(f);
			break;
		case Decimal:
			int dlen = 0;
			dlen = readB4Value(stream, flagBts[0], dlen);
			if (dlen > 0) {
				byte[] tmpBts = new byte[dlen];
				if (tmpBts.length != stream.read(tmpBts, 0, tmpBts.length)) {
					// return false;
				}
				value = new BigDecimal(new BigInteger(tmpBts));
			}
			break;
		case Char:
			Short outValue = 0;
			value = (char) readB2Value(stream, flagBts[0], outValue);
			break;
		case String:
			int len = 0;
			len = readB4Value(stream, flagBts[0], len);
			if (len > 0) {
				byte[] tmpBts = new byte[len];
				if (tmpBts.length != stream.read(tmpBts, 0, tmpBts.length)) {
					// return false;
				}
				value = new String(tmpBts, "UTF-8");
			}
			break;
		case Date:
			long outdValue = 0;
			outdValue = readB8Value(stream, flagBts[0], outdValue);
			value = new Date(outdValue);
			break;
		case Time:
			long outtValue = 0;
			value = new Time(readB8Value(stream, flagBts[0], outtValue));
			break;
		case TimeSpan:
			long outttsValue = 0;
			value = new java.sql.Timestamp(readB8Value(stream, flagBts[0],
					outttsValue));
			break;
		case Bytes:
			int blen = 0;
			blen = readB4Value(stream, flagBts[0], blen);
			if (blen >= 0) {
				byte[] tmpBts = new byte[blen];
				if (tmpBts.length != stream.read(tmpBts, 0, tmpBts.length)) {

				}
				value = tmpBts;
			}

			break;
		case Object:
			int bobjectlen = 0;
			blen = readB4Value(stream, flagBts[0], bobjectlen);
			if (blen >= 0) {
				byte[] tmpBts = new byte[blen];
				if (tmpBts.length != stream.read(tmpBts, 0, tmpBts.length)) {

				}
				value = toObject(tmpBts);
			}

			break;
		}
		return value;
	}

	private static void writeB2Value(ByteArrayOutputStream stream, short n)
			throws IOException {
		if ((n | 0x00FF) == 0x00FF) {
			// 0x80表示小于0xFF
			stream.write(0x80);
			stream.write((byte) n);
		} else {
			// 0x81表示大于0xFF
			stream.write(0x81);
			byte[] buffer = new byte[2];
			buffer[0] = (byte) (0xff & (n >> 8));
			buffer[1] = (byte) (0xff & n);
			stream.write(buffer, 0, 2);
		}
	}

	public static void writeB4Value(ByteArrayOutputStream stream, int n)
			throws IOException {
		byte[] buffer = new byte[4];
		/*
		 * if ((n | 0x000000FF) == 0x000000FF) { //0x80表示小于0xFF
		 * stream.write(0x80); stream.write((byte)n); } else if ((n |
		 * 0x0000FFFF) == 0x0000FFFF) { //0x81表示大于0xFF且小于0x7FFF
		 * stream.write(0x81); buffer[0] = (byte)(0xff & (n >> 8)); buffer[1] =
		 * (byte)(0xff & n); stream.write(buffer, 0, 2); } else {
		 */
		// 0x82表示其它情况
		stream.write(0x82);
		buffer[0] = (byte) (0xff & (n >> 24));
		buffer[1] = (byte) (0xff & (n >> 16));
		buffer[2] = (byte) (0xff & (n >> 8));
		buffer[3] = (byte) (0xff & n);
		stream.write(buffer, 0, 4);

	}

	private static void writeB8Value(ByteArrayOutputStream stream, long n)
			throws IOException {
		byte[] buffer = new byte[8];
		// 0x83表示其它情况
		stream.write(0x83);
		buffer[0] = (byte) (0xff & (n >> 56));
		buffer[1] = (byte) (0xff & (n >> 48));
		buffer[2] = (byte) (0xff & (n >> 40));
		buffer[3] = (byte) (0xff & (n >> 32));
		buffer[4] = (byte) (0xff & (n >> 24));
		buffer[5] = (byte) (0xff & (n >> 16));
		buffer[6] = (byte) (0xff & (n >> 8));
		buffer[7] = (byte) (0xff & n);
		stream.write(buffer, 0, 8);
	}

	private static short readB2Value(ByteArrayInputStream stream, byte flag,
			Short value) {
		value = 0;
		byte[] buffer = new byte[2];
		if (flag == (byte) 0x80) {
			if (1 != stream.read(buffer, 0, 1)) {
				// return false;
			}
			value = (short) buffer[0];
		} else {
			if (2 != stream.read(buffer, 0, 2)) {
				// return false;
			}
			value = (short) (((buffer[0] & 0xff) << 8) | ((buffer[1] & 0xff)));
		}
		return value;
	}

	private static int readB4Value(ByteArrayInputStream stream, byte flag,
			int value) {
		value = 0;
		byte[] buffer = new byte[4];
		if (flag == (byte) 0x80) {
			if (1 != stream.read(buffer, 0, 1)) {
				// return false;
			}
			value = (int) buffer[0];
		} else if (flag == (byte) 0x81) {
			if (2 != stream.read(buffer, 0, 2)) {
				// return false;
			}
			value = (int) (((buffer[0] & 0xff) << 8) | ((buffer[1] & 0xff)));
		} else {
			if (4 != stream.read(buffer, 0, 4)) {
				// return false;
			}
			value = (int) (((buffer[0] & 0xff) << 24)
					| ((buffer[1] & 0xff) << 16) | ((buffer[2] & 0xff) << 8) | ((buffer[3] & 0xff)));
		}
		return value;
	}

	private static long readB8Value(ByteArrayInputStream stream, byte flag,
			long value) {
		value = 0;
		byte[] buffer = new byte[8];
		if (flag == (byte) 0x80) {
			if (1 != stream.read(buffer, 0, 1)) {
				// return false;
			}
			value = (long) buffer[0];
		} else if (flag == (byte) 0x81) {
			if (2 != stream.read(buffer, 0, 2)) {
				// return false;
			}
			value = (long) (((buffer[0] & 0xff) << 8) | ((buffer[1] & 0xff)));

		} else if (flag == (byte) 0x82) {
			if (4 != stream.read(buffer, 0, 4)) {
				// return false;
			}
			value = (long) (((buffer[0] & 0xff) << 24)
					| ((buffer[1] & 0xff) << 16) | ((buffer[2] & 0xff) << 8) | ((buffer[3] & 0xff)));
		} else {
			if (8 != stream.read(buffer, 0, 8)) {
				// return false;
			}
			value = (long) (((long) (buffer[0] & 0xff) << 56)
					| ((long) (buffer[1] & 0xff) << 48)
					| ((long) (buffer[2] & 0xff) << 40)
					| ((long) (buffer[3] & 0xff) << 32)
					| ((long) (buffer[4] & 0xff) << 24)
					| ((long) (buffer[5] & 0xff) << 16)
					| ((long) (buffer[6] & 0xff) << 8) | ((long) (buffer[7] & 0xff)));
		}
		return value;
	}

	public static DataType TypeToDataType(String typename) {
		if (typename==null) return null;
		if (typename.toUpperCase().equals("TINYINT")) {
			return DataType.Int16;
		}
		if (typename.toUpperCase().equals("SMALLINT")) {
			return DataType.Int16;
		}
		if (typename.toUpperCase().equals( "MEDIUMINT")) {
			return DataType.Int32;
		}
		if (typename.toUpperCase().equals( "INT") ){
			return DataType.Int32;
		}
		if (typename.toUpperCase().equals( "INT UNSIGNED")) {
			return DataType.Int64;
		}
		if (typename.toUpperCase().equals( "BIGINT")) {
			return DataType.Int64;
		}
		if (typename.toUpperCase().equals( "BIT")) {
			return DataType.Bool;
		}
		if (typename.toUpperCase().equals( "DOUBLE")) {
			return DataType.Double;
		}
		if (typename.toUpperCase().equals( "FLOAT")) {
			return DataType.Float;
		}
		if (typename.toUpperCase().equals( "DECIMAL")) {
			return DataType.String;
			// return DataType.Decimal;
		}
		if (typename.toUpperCase().equals( "CHAR")) {
			return DataType.String;
		}
		if (typename.toUpperCase().equals( "VARCHAR")) {
			return DataType.String;
		}
		if (typename.toUpperCase().equals( "NVARCHAR")) {
			return DataType.String;
		}
		if (typename.toUpperCase().equals( "TEXT")) {
			return DataType.String;
		}
		if (typename.toUpperCase().equals( "DATE")) {
			return DataType.Date;
		}
		if (typename.toUpperCase().equals( "TIME")) {
			return DataType.Time;
		}
		if (typename.toUpperCase().equals( "YEAR")) {
			return DataType.Date;
		}
		if (typename.toUpperCase().equals( "TIMESTAMP")) {
			return DataType.TimeSpan;
		}
		if (typename.toUpperCase().equals( "DATETIME") ){
			return DataType.TimeSpan;
		}
		if (typename.toUpperCase().equals( "TINYBLOB")) {
			return DataType.Bytes;
		}
		if (typename.toUpperCase().equals( "BLOB")) {
			return DataType.Bytes;
		}
		if (typename.toUpperCase().equals( "MEDIUMBLOB")) {
			return DataType.Bytes;
		}
		if (typename.toUpperCase().equals( "LONGBLOB")) {
			return DataType.Bytes;
		}
		if (typename.toUpperCase().equals( "BINARY")) {
			return DataType.Bytes;
		}
		if (typename.toUpperCase().equals("VARBINARY")) {
			return DataType.Bool;
		}
		if (typename.toUpperCase().equals( "GEOMETRY")) {
			return DataType.Bytes;
		}
		return null;
	}

	public static <T> DataType TypeToDataType(T type) {
		if (type.toString().equals("short")) {
			return DataType.Int16;
		}		
		if (type.toString().equals("int")) {
			return DataType.Int32;
		}
		if (type.toString().equals("class java.lang.Integer")) {
			return DataType.Int32;
		}
		if (type.toString().equals("long")) {
			return DataType.Int64;
		}
		if (type.toString().equals("boolean")) {
			return DataType.Bool;
		}
		if (type.toString().equals("double")) {
			return DataType.Double;
		}
		if (type.toString().equals("float")) {
			return DataType.Float;
		}
		if (type.toString().equals("class java.math.BigDecimal")) {
			return DataType.Decimal;
		}
		if (type.toString().equals("class java.lang.String")) {
			return DataType.String;
		}
		if (type.toString().equals("class java.sql.Date")) {
			return DataType.Date;
		}
		if (type.toString().equals("class java.sql.Time")) {
			return DataType.Time;
		}
		if (type.toString().equals("class java.sql.Timestamp")) {
			return DataType.TimeSpan;
		}
		if (type.toString().equals("class [B")) {
			return DataType.Bytes;
		}
		return DataType.Array;
	}

	public static Type DataTypeToType(DataType type) {
		switch (type) {
		case Int16:
			return short.class;
		case Int32:
			return int.class;
		case Int64:
			return long.class;
		case Bool:
			return boolean.class;
		case Double:
			return double.class;
		case Float:
			return float.class;
		case Decimal:
			return BigDecimal.class;
		case Char:
			return String.class;
		case String:
			return String.class;
		case Date:
			return java.sql.Date.class;
		case Time:
			return Time.class;
		case TimeSpan:
			return java.sql.Timestamp.class;
		case Bytes:
			return byte[].class;
		}
		return Object.class;
	}

	/**
	 * 对象转数组
	 * 
	 * @param obj
	 * @return
	 */
	public static byte[] toByteArray(Object obj) {
		byte[] bytes = null;
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		try {
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(obj);
			oos.flush();
			bytes = bos.toByteArray();
			oos.close();
			bos.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		}
		return bytes;
	}

	/**
	 * 数组转对象
	 * 
	 * @param bytes
	 * @return
	 */
	public static Object toObject(byte[] bytes) {
		Object obj = null;
		try {
			ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
			ObjectInputStream ois = new ObjectInputStream(bis);
			obj = ois.readObject();
			ois.close();
			bis.close();
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (ClassNotFoundException ex) {
			ex.printStackTrace();
		}
		return obj;
	}
}
